#include "Dri_ADC.h"

/**
 * @brief ADC 初始化
 *
 */
void Dri_ADC_Init(void)
{
    // 1. 时钟设置 ------------------------------------------------
    // 1.1 开启 ADC 时钟使能
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    // 1.2 ADC 时钟分频设置，进行6分频，得到12MHz (RCC_CFGR.ADCPRE=10)
    RCC->CFGR |= RCC_CFGR_ADCPRE_1;
    RCC->CFGR &= ~RCC_CFGR_ADCPRE_0;
    // 1.3 开启 GPIOC 时钟使能
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    // 1.4 开启 DMA 时钟使能
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    // 2. GPIO 设置 -----------------------------------------------
    // 2.1 ADC1_IN0 （PC0）设置为模拟输入, MODE=00, CNF00
    GPIOC->CRL &= ~(GPIO_CRL_MODE0 | GPIO_CRL_CNF0);
    // 2.2 ADC1_IN12 (PC2) 设置为模拟输入, MODE=00, CNF02
    GPIOC->CRL &= ~(GPIO_CRL_MODE2 | GPIO_CRL_CNF2);

    // 3. ADC1 设置 ------------------------------------------------
    // 3.1 设置 ADC1_IN10、ADC1_IN12 的采样时间为 71.5 周期 (ADC1_SMPR1.SMP10=110)
    ADC1->SMPR1 |= (ADC_SMPR1_SMP10_2 | ADC_SMPR1_SMP12_2);
    ADC1->SMPR1 |= (ADC_SMPR1_SMP10_1 | ADC_SMPR1_SMP12_1);
    ADC1->SMPR1 &= ~(ADC_SMPR1_SMP10_0 | ADC_SMPR1_SMP12_0);
    // 3.2 设置规则组序列长度为2 (ADC1_SQR1.L=0001)
    ADC1->SQR1 &= ~ADC_SQR1_L;
    ADC1->SQR1 |= ADC_SQR1_L_0;
    // 3.3 将 ADC1_IN10 放入规则组序列中的第一个位置 (ADC1_SQR3.SQ1=01010)
    ADC1->SQR3 &= ~ADC_SQR3_SQ1; // 将SQ1对应的4个位置0
    ADC1->SQR3 |= (10 << 0);     // 将 10 放入 SQ1 的位置上
    // 3.4 将 ADC1_IN12 放入规则组序列中的第二个位置 (ADC1_SQR3.SQ2=01100)
    ADC1->SQR3 &= ~ADC_SQR3_SQ2; // 将SQ2对应的4个位置0
    ADC1->SQR3 |= (12 << 5);     // 将 12 放入 SQ2 的位置上
    // 3.5 设置规则组序列开启扫描模式 (ADC1_CR1.SCAN=1)
    ADC1->CR1 |= ADC_CR1_SCAN;
    // 3.6 设置规则组序列开启连续转换模式 (ADC1_CR2.CONT=1)
    ADC1->CR2 |= ADC_CR2_CONT;
    // 3.7 设置关闭规则组的外部事件触发 (ADC1_CR2.EXTTRIG=0)
    ADC1->CR2 &= ~ADC_CR2_EXTTRIG;
    // 3.8 设置规则组序列的数据寄存器对齐方式为右对齐 (ADC1_CR2.ALIGN=0)
    ADC1->CR2 &= ~ADC_CR2_ALIGN;
    // 3.9 设置开启DMA模式
    ADC1->CR2 |= ADC_CR2_DMA;

    // 4. DMA1 设置 ---------------------------------------------------
    // 4.1 设置传输方向为外设到RAM (DMA1_CCR1.DIR=0)
    DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;
    // 4.2 设置外设(ADC1)的数据位宽为16位 (DMA1_CCR1.MSIZE=01)
    DMA1_Channel1->CCR &= ~DMA_CCR1_MSIZE_1;
    DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0;
    // 4.3 设置存储器(RAM)的数据位宽为16位 (DMA1_CCR1.PSIZE=01)
    DMA1_Channel1->CCR &= ~DMA_CCR1_PSIZE_1;
    DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0;
    // 4.4 设置外设(ADC1)不开启地址增量模式 (DMA1_CCR1.PINC=0)
    DMA1_Channel1->CCR &= ~DMA_CCR1_PINC;
    // 4.5 设置存储器(RAM)开启地址增量模式 (DMA1_CCR1.MINC=1)
    DMA1_Channel1->CCR |= DMA_CCR1_MINC;
    // 4.6 设置DMA1_Channel1的优先级为高 (DMA1_CCR1.PL=11)
    DMA1_Channel1->CCR |= DMA_CCR1_PL;
    // 4.7 设置开启循环模式 (DMA1_CCR1.CIRC=1)
    DMA1_Channel1->CCR |= DMA_CCR1_CIRC;
}

/**
 * @brief ADC 启动转换
 *
 */
void Dri_ADC_Start(void)
{
    // 1. 唤醒 ADC (ADON是0的时候置1)
    ADC1->CR2 |= ADC_CR2_ADON;

    // 2. 开启校准并等待校准完成 (同时ADC稳定时间会过去)
    ADC1->CR2 |= ADC_CR2_CAL;
    while (ADC1->CR2 & ADC_CR2_CAL)
        ;

    // 3. 启动 ADC 转换（ADON是1的时候置1）
    ADC1->CR2 |= ADC_CR2_ADON;

    // 4. 等待 ADC 转换结束
    while (!(ADC1->SR & ADC_SR_EOC))
        ;
}

/**
 * @brief ADC 启动DMA传输
 * 
 * @param m_addr 要传输到的RAM地址
 */
void Dri_ADC_Start_DMA(uint32_t m_addr)
{
    // 1. 设置外设地址
    DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;

    // 2. 设置存储器地址
    DMA1_Channel1->CMAR = m_addr;

    // 3. 设置DMA传输长度
    DMA1_Channel1->CNDTR = 2; 

    // 4. 使能DMA传输
    DMA1_Channel1->CCR |= DMA_CCR1_EN;

}

/**
 * @brief 返回测得的电压
 * 
 * @return float 电压值，范围为 0.0 到 3.3 伏特
 */
float Dri_ADC_GetVoltage(void)
{
    return ADC1->DR / 4095.0 * 3.3;
}
